package com.um.jdy.common.charge.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.um.jdy.common.base.enums.YesOrNo;
import com.um.jdy.common.charge.dao.OrderPlaceDao;
import com.um.jdy.common.charge.dao.OrderPowerDao;
import com.um.jdy.common.charge.entity.po.*;
import com.um.jdy.common.charge.entity.vo.EboxOrderVO;
import com.um.jdy.common.charge.entity.vo.EvOrderVO;
import com.um.jdy.common.charge.entity.vo.OrderEnergyItem;
import com.um.jdy.common.charge.entity.vo.OrderVO;
import com.um.jdy.common.charge.enums.OrderAction;
import com.um.jdy.common.charge.enums.OrderEnum;
import com.um.jdy.common.charge.service.*;
import com.um.jdy.common.device.entity.po.Pile;
import com.um.jdy.common.device.enums.DeviceEnum;
import com.um.jdy.common.device.service.PileService;
import com.um.jdy.rpc.enums.DeviceTypeEnum;
import com.um.jdy.rpc.factory.ServiceFactory;
import com.um.springboot.starter.utils.StringUtil;
import com.um.springboot.starter.utils.UMBeanUtils;
import com.um.springcloud.dubbo.cluster.DeviceKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import com.um.springboot.starter.orm.service.BaseEntityServiceImpl;
import com.um.jdy.common.charge.dao.OrderDao;

import java.util.Date;
import java.util.List;
import java.util.Random;

/**
 * 服务实现类：充电订单表
 */
@Service
@Slf4j
public class OrderServiceImpl extends BaseEntityServiceImpl<OrderDao,Order>  implements OrderService{

    @Autowired
    OrderPlaceDao orderPlaceDao;

    @Autowired
    EboxOrderService eboxOrderService;

    @Autowired
    EvOrderService evOrderService;

    @Autowired
    PbOrderService pbOrderService;

    @Autowired
    OrderPowerDao orderPowerDao;

    @Autowired
    PileService pileService;

    @Autowired
    DataLogService dataLogService;

    @Autowired
    ServiceFactory serviceFactory;

    @Autowired
    TaskScheduler taskScheduler;

    /**
     * 当前充电电量
     * @param orderId
     * @return
     */
    @Override
    public int getCapacity(String orderId){
        long beginEnergy = getBeginEnergy(orderId);
        long endEnergy = getEndEnergy(orderId,beginEnergy);
        int chargeCapacity = (int)(endEnergy - beginEnergy);
        return chargeCapacity;
    }

    /**
     * 查询电费+服务费总额
     * @param order
     * @param energyPrice
     * @param servicePrice
     * @return
     */
    @Override
    public int getEnergyAndServiceFee(Order order,int energyPrice,int servicePrice,int second){
        return getTimeEnergyFee(order,energyPrice)+getTimeServiceFee(order,servicePrice,second);
    }

    /**
     * 查询电费
     * @param order
     * @param energyPrice
     * @return
     */
    @Override
    public int getTimeEnergyFee(Order order,int energyPrice){
        int chargeCapacity = getCapacity(order.getChargeOrderId());
        int energyFee = (int)(energyPrice * chargeCapacity / 1000.0);
        return energyFee;
    }

    /**
     * 查询服务费：充电中服务费按实际时长来计算
     * @param order
     * @param servicePrice
     * @return
     */
    @Override
    public int getTimeServiceFee(Order order,int servicePrice,int second){
        //int second = (int)DateUtil.between(order.getStartTime(),order.getEndTime() != null ? order.getEndTime() : new Date(),DateUnit.SECOND);
        int serviceFee = (int)(servicePrice * (second / 60) * 60 / 3600.0);
        return serviceFee;
    }

    /**
     * 查询充电电费的费用
     * @param orderId
     * @return
     */
    @Override
    public int getEnergyFee(String orderId){
        Long energyFee = this.getCountBySqlName("get_order_energy_fee", new Object[]{orderId});
        return energyFee.intValue();
    }

    /**
     * 查询充电服务费的费用
     * @param orderId
     * @return
     */
    @Override
    public int getServiceFee(String orderId){
        //if(StringUtil.equals(serviceType,OrderEnum.FeeType.Energy.name())) {
            Long energyFee = this.getCountBySqlName("get_order_service_fee", new Object[]{orderId});
            return energyFee.intValue();
//        }else{
//            Long energyFee = this.getCountBySqlName("get_order_time_service_fee", new Object[]{orderId});
//            return energyFee.intValue();
//        }
    }

    /**
     * 获取订单充电时间（秒）
     *
     * @param order
     * @param currentTime
     * @return
     */
    @Override
    public Integer getOrderChargeSeconds(Order order, Date currentTime) {
        int paySeconds = order.getPaySeconds();
        Date startTime = order.getCreateTime();
        int chargeSeconds = (int)Math.max(0,(currentTime.getTime() - startTime.getTime())/1000);
        return Math.min(paySeconds, chargeSeconds);

    }

    /**
     * 查询充电时段费用明细
     * @param orderId
     * @return
     */
    @Override
    public List<OrderEnergyItem> getOrderFeeList(String orderId){
        //if(StringUtil.equals(serviceType,OrderEnum.FeeType.Energy.name())) {
            return this.getViewModelList("get_order_energy_and_service_fee", OrderEnergyItem.class, new Object[]{orderId});
//        }else{
//            return this.getViewModelList("get_order_energy_and_time_service_fee", OrderEnergyItem.class, new Object[]{orderId});
//        }
    }

    /**
     * 查询开始电表值
     * @param orderId
     * @return
     */
    @Override
    public Long getBeginEnergy(String orderId){
        OrderPower item = orderPowerDao.getEntityBySqlName("get_order_begin_energy",new Object[]{orderId});
        if(item == null){
            return 0l;
        }else{
            return  item.getEnergy();
        }
    }

    /**
     * 查询最后的电量
     * @param orderId
     * @return
     */
    @Override
    public Long getEndEnergy(String orderId,Long beginEnergy){
        OrderPower item = orderPowerDao.getEntityBySqlName("get_order_end_energy",new Object[]{orderId});
        if(item == null){
            return beginEnergy;
        }else{
            try {
                return Math.max(beginEnergy, item.getEnergy());
            }catch (Exception ex){
                log.error("查询最后电量出错：{}",ex.getMessage());
                return beginEnergy;
            }
        }
    }


    @Override
    public void calcEVOrderPlaceFee(OrderVO order, EvOrderVO evOrder){
        OrderPlace orderPlace = orderPlaceDao.getRow("charge_order_id", order.getChargeOrderId());
        if(null != orderPlace){
            evOrder.setPlaceEndTime(DateUtil.date());
            long between = DateUtil.between(evOrder.getPlaceBeginTime(), evOrder.getPlaceEndTime(), DateUnit.SECOND);
            evOrder.setPlaceSeconds((int) Math.max(0, between - orderPlace.getFreeDuration() * 60));

            if (orderPlace.getStatus().equals(YesOrNo.Yes.name()) && evOrder.getPlaceSeconds() > 0) {//收占位费
                evOrder.setPlaceAmount(evOrderService.savePlaceFee(orderPlace, BeanUtil.toBean(evOrder, EvOrder.class), false));
            }

            order.setOrderAmount(order.getOrderAmount() + evOrder.getPlaceAmount());
        }
    }

    @Override
    public void calcEBoxOrderPlaceFee(OrderVO order, EboxOrderVO eboxOrder){
        OrderPlace orderPlace = orderPlaceDao.getRow("charge_order_id", order.getChargeOrderId());
        if(null != orderPlace){
            eboxOrder.setPlaceSeconds(eboxOrderService.getPlaceSeconds(orderPlace, UMBeanUtils.copyBean(eboxOrder, EboxOrder.class)));
            eboxOrder.setPlaceAmount(eboxOrderService.getPlaceAmount(orderPlace,UMBeanUtils.copyBean(eboxOrder,EboxOrder.class)));
            order.setOrderAmount(order.getChargeAmount() + eboxOrder.getPlaceAmount());
        }
    }

    /**
     * 实时计算时长和费用
     * @param orderVO
     */
    @Override
    public void setOrderDurationAndAmount(OrderVO orderVO){

        if (null == orderVO.getStartTime()) {
            orderVO.setDuration(0);
        } else {
            Date startTime = orderVO.getStartTime();
            long seconds = DateUtil.between(startTime, DateUtil.date(), DateUnit.SECOND, false);
            orderVO.setDuration((int) Math.max(0, seconds));
        }

        if(orderVO.getDeviceType().equals("PB")){
                PbOrder pbOrder = pbOrderService.getEntity("charge_order_id",orderVO.getChargeOrderId());
                orderVO.setChargeAmount(pbOrderService.getChargeAmount(pbOrder,orderVO.getDuration()));
                orderVO.setOrderAmount(orderVO.getChargeAmount());
        }

    }

    /**
     * 更新充电桩的用户余额
     * @param userId
     * @param cardNo
     * @param balance
     */
    @Override
    public void remoteUpdateEVChargeBalance(String userId,String cardNo,Integer balance) {

        Order order = this.getRow(new String[]{"member_user_id","status","device_type"},new Object[]{userId, OrderEnum.Status.Charging.name(), DeviceTypeEnum.EV.name()});
        if(order != null) {
            taskScheduler.schedule(() -> scheduleUpdateEVChargeBalance(order.getDeviceCode(),order.getPort(),order.getChargeOrderId(),cardNo,balance),
                    DateUtil.date().offset(DateField.SECOND, 10));
        }
    }

    @Override
    public Long getUnFinishEVOrders(String userId, String comId) {
        return this.getCountBySqlName("get_unfinish_ev_order_num",new Object[]{userId,comId});
    }

    private void scheduleUpdateEVChargeBalance(String deviceCode,Integer port,String orderId,String cardNo,Integer balance){
        Pile pile = pileService.getRow("code",deviceCode);
        if(pile == null)
            return;

        if (!StringUtil.equals(pile.getDeviceStatus(), DeviceEnum.DeviceStatus.Online.name())) {
            return;
        }

        try {
            dataLogService.saveChargeData(pile.getComId(),orderId, OrderAction.EV.UpdateBalance,new Date(),
                    MapUtil.builder().put("imei", pile.getImei()).put("port", port).put("cardNo", cardNo).put("balance", balance).build(),
                    "下发更新订单余额！");
            serviceFactory.getRpcEVDeviceService(pile.getProtocol()).updateBalance(new DeviceKey(pile.getImei()),port,cardNo,balance);
            log.error("更新价格成功：{}",pile.getCode());
        }catch (Exception ex){
            log.error("更新价格失败：{}-{}",pile.getCode(),ex.getMessage());
            dataLogService.saveChargeData(pile.getComId(),orderId, OrderAction.EV.UpdateBalance,new Date(),new Object(),"下发更新订单余额失败！");
        }
    }
}
